package com.darkflame.client.semantic;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Logger;

import com.darkflame.client.SuperSimpleSemantics;
import com.darkflame.client.Utilitys;
import com.darkflame.client.interfaces.GenericDebugDisplayer;
import com.darkflame.client.interfaces.GenericIndexDisplayer;
import com.darkflame.client.interfaces.SSSGenericFileManager.FileCallbackError;
import com.darkflame.client.interfaces.SSSGenericFileManager.FileCallbackRunnable;
import com.darkflame.client.interfaces.GenericWaitForRepeating.MyRepeatingCommand;

/**
 * An SSSIndex is a special set of triplets that define files containing all the
 * same property The object in this case is always a url.
 * 
 * The file format parsed into this is very close to n3
 * 
 * subject property value;
 * 		   property value;
 * 		   property value.
 * 
 * anothersubject property value.
 * 
 * Subject,property and values can be specified either with a full uri, or a prefix
 * in the form word:name (eg dbpedia:apple) where the word is just a short name for a base uri
 * and the name is the node name (ie, whats normally after the #)
 * 
 * Quoted strings are used for allow values with spaces in.
 * 
 * The most significant different .sss have from .n3 files is the subject can be a location of a text file,
 * then everything in that text file will be given those propertys and values
 * 
 * 
 **/
public class SSSIndex extends SSSTripletList {
	
	/**hashset of url's to be loaded */
	private static final HashSet<String> IndexsToLoadList = new HashSet<String>();
	
	static Logger Log = Logger.getLogger("sss.SSSIndex");
	private static GenericDebugDisplayer DebugPanel=null;
	
	public static void setIndexDisplayer(GenericIndexDisplayer indexDisplayer) {
		IndexDisplayer = indexDisplayer;
	}
	private static GenericIndexDisplayer  IndexDisplayer = null;
	
	//variables to track loading of indexes
	//private int LeftToLoad = 0;
	
	//private AsyncCallback<String> calledWhenDone;
		
	private static int globalIndexsLeftToLoad = 0;
	
	public static int getGlobalIndexsLeftToLoad() {
		return globalIndexsLeftToLoad;
	}
	
	public static void addToIndexsLeftToLoad(int i) {
		
		globalIndexsLeftToLoad=globalIndexsLeftToLoad+i;
		
	}
	
	//private static AsyncCallback<String> 
	private static LoadingCallback globalCalledWhenDone;
	
	
	/**
	 * set to true after the index is processed and its all ready to use
	 * Note; This does not mean all its nodes are parsed (because those are likely also files to be loaded)
	 * In future there should probably be a per-index flag to represent if the Indexs nodes are all passed and ready
	 * At the moment this has to be handled in a database global way
	 */
	boolean indexLoadedAndProccessed = false;

	/**
	 * returns true if the index is loaded and ready  to use
	 * @return
	 */
	public boolean isIndexLoaded() {
		return indexLoadedAndProccessed;
	}
	
	String IndexsDefaultNameSpace = "";
	
	public String getIndexsDefaultNameSpace() {
		return IndexsDefaultNameSpace;
	}
	
	String IndexsDefaultDirectory="";
	
	/** The directory this index was found in.
	 * This becomes the assumed relative path of all files */
	public String getIndexsDefaultDirectory() {
		return IndexsDefaultDirectory;
	}
	public SSSIndex currentIndex= this;
	
	static HashSet<SSSIndex> allKnowenIndexs = new HashSet<SSSIndex>();
	
	//url of index
	public String IndexURL="";
		
		
	/** sets a callback to run after all loading is done AsyncCallback<String> **/
	public static void setGlobalCallback(LoadingCallback calledWhenDone) {
		
		globalCalledWhenDone = calledWhenDone;

	}
	
	public static SSSIndex createNewIndex(final String fileURL){	
		
		if (allKnowenIndexs.contains(fileURL)){ //||(IndexsToLoadList.contains(fileURL)) (no longer checked as things are added to loading before reaching this point
			
			Log.warning("Index "+fileURL+" already loaded, so not doing anything!");
			
			
			return null;
		}
		
		return new SSSIndex(fileURL);		
	}
	

	public SSSIndex(final String fileURL, final String defaultNS) {
		IndexsDefaultNameSpace = defaultNS;
		
		IndexsDefaultDirectory = defaultNS; 
		
		setDefaultDirectoryFromFileURL(fileURL);
		
	}

	private void setDefaultDirectoryFromFileURL(final String fileURL) {
		if (fileURL.contains("/") || fileURL.contains("\\") ){
			
			String croppedfile =fileURL;
			
			//as we know at least one is present we can just look or the largest index
			int endslash = 0;
			int endslash2 =0;
			
			//remove to /
			if (croppedfile.contains("/")){
				endslash =  fileURL.lastIndexOf("/");
			}
			//remove to \
			if (croppedfile.contains("\\")){
				endslash2 = croppedfile.lastIndexOf("\\");
				
			}
			//using the maths command
			endslash = Math.max(endslash, endslash2);
			
			croppedfile = croppedfile.substring(0, endslash);
						
			IndexsDefaultDirectory = croppedfile + "/";
									
		
		}
	}
	
	/**
	 * should not be used directly. use SSSIndex.createNewIndex to safely avoid recreation of existing ones
	 * @param fileURL
	 */
	public SSSIndex(final String fileURL) {
		//ensure its added to the loading list 
		//(this is somewhat redundant, as normally indexs are loaded with the "loadIndexsAt" function
		//however, we repeat here in case some have been added individually)
		IndexsToLoadList.add(fileURL);
		
		IndexURL=fileURL;		

		Log.info("loading: "+fileURL);
		
		//set default directory from file url
		setDefaultDirectoryFromFileURL(fileURL);
		
		// set IndexsDefaultNameSpace if its not already set
		if (IndexsDefaultNameSpace.equals("")){
			
			
			//we now just use the URL of this file
			//This allows a few different namespaces on the same server
			//I am still hesitant if this is ok, but it clearly has advantages;
			IndexsDefaultNameSpace = fileURL+"#";
				
			
			/*	
			//if there's a / in the name, we get it from the portion of the fileURL before the last slash;
			//eg \semantics\TomsNetwork.ntlist
			if (fileURL.contains("/") || fileURL.contains("\\") ){
				
				String croppedfile =fileURL;
				
				//as we know at least one is present we can just look or the largest index
				int endslash = 0;
				int endslash2 =0;
				
				//remove to /
				if (croppedfile.contains("/")){
					endslash =  fileURL.lastIndexOf("/");
				}
				//remove to \
				if (croppedfile.contains("\\")){
					endslash2 = croppedfile.lastIndexOf("\\");
					
				}
				//using the maths command
				endslash = Math.max(endslash, endslash2);
				
				croppedfile = croppedfile.substring(0, endslash);
				
				//the following line is wrong
				//we should assume the current indexs filename instead of "defaultOntology"
				//...umm...I think
				IndexsDefaultNameSpace = croppedfile + "/DefaultOntology.n3#";
				
				IndexsDefaultDirectory = croppedfile + "/";
				
				
				
			
			} else {
			
				//there's no location specified so we need to use the global default
				IndexsDefaultNameSpace = SuperSimpleSemantics.getDefaultBaseURI();
			    IndexsDefaultDirectory = SuperSimpleSemantics.getDefaultBaseURI();
				
			}*/
			
		}
		
		// load from url

	//	LeftToLoad++;
		globalIndexsLeftToLoad++;
		
		Log.warning("global indexs left to load increased:"+globalIndexsLeftToLoad);

		Log.info("IndexsDefaultDirectory: "+IndexsDefaultDirectory);
		
		//set up post load commands callback

		final Runnable postIndexProcessFunctions = new Runnable(){
			
			@Override
			public void run() {
				
				Log.info("index:"+currentIndex.IndexURL+" loaded, adding to known index list");
				allKnowenIndexs.add(currentIndex);	
				
				if (IndexDisplayer!=null){
					IndexDisplayer.addIndexToDisplayer(currentIndex);
				}
				// keep track of pending file loads?
				// LeftToLoad--;
				globalIndexsLeftToLoad--;
				IndexsToLoadList.remove(IndexURL);
				
				Log.warning("global indexs left to load :::::::::::::::::: " + globalIndexsLeftToLoad);

				//sub from loading widget
				//TrustedIndexList.loadingicon.stepClockForward();
				SuperSimpleSemantics.stepLoadClockForward();
				
				
				//
				// if (LeftToLoad == 0 && !NodeListActive) {
				//
				// globalNodesWithPropertyListByPredicate.put(commonPrec,
				// thisset);
				// NodeListActive = true;
				// }

				// if (LeftToLoad == 0) {
				// // activatecallback
				// if (calledWhenDone != null) {
				// calledWhenDone.onSuccess("loaded:" + fileURL);
				// }
				// }
				
				
				//set as loaded
				indexLoadedAndProccessed = true;
				
				if (IndexsToLoadList.size()==0){
					Log.warning("******************All Indexs Loaded");
					if (globalCalledWhenDone != null) {

						globalCalledWhenDone.onSuccess("loaded all");
					}
				}
				/*
				if (globalIndexsLeftToLoad == 0) {
					if (globalCalledWhenDone != null) {

						globalCalledWhenDone.onSuccess("loaded all");
					}
					
				}*/
			}
			
		};

		Log.info("Loading icon updating");
		//add to loading widget
		SuperSimpleSemantics.addToTotalLoadUnits(1);
		SuperSimpleSemantics.setCurrentLoadProcess("Loading index at:"+fileURL);
		
		//on error handler
		
		//on response
		
		//fire the request
		
		FileCallbackRunnable onResponse = new FileCallbackRunnable(){

			@Override
			public void run(final String responseData, int responseCode) { 
				
				
				//String response = responseData;
				SuperSimpleSemantics.setCurrentLoadProcess("Recieved file");
				

				if (responseCode==404){
					
					Log.info("no index file found ");
					
					//LeftToLoad--;
					globalIndexsLeftToLoad--;
					IndexsToLoadList.remove(IndexURL);
					
					//sub from loading widget
					SuperSimpleSemantics.stepLoadClockForward();
					
					return;
				}
				
				Log.info("index file found " + responseData);

				// process file
				
				//we have this deferred to give the GUI ( and thus loading icon) time
				// to update.
				SuperSimpleSemantics.waitFor.scheduleAfter(new Runnable() {
					
					@Override
					public void run() {
						SuperSimpleSemantics.setCurrentLoadProcess("processing recieved index file");
						
						processIndexFile(responseData);
					}
				});
				
						/*			
				Scheduler.get().scheduleDeferred(new ScheduledCommand() {    					
					@Override
					public void execute() {
						TrustedIndexList.loadingicon.setCurrentProcess("processing recieved index file");
						
							processIndexFile(responseData);
					}});*/
				
			}

			public void processIndexFile(String response) {
				
				
				// the domain should be the default namespace for this set
				// of nodes					
				parseSSSIndexFile(response, IndexsDefaultNameSpace, postIndexProcessFunctions);
				
				
				
			}

			
			
		};

		//what to do if there's an error
		FileCallbackError onError = new FileCallbackError(){

			@Override
			public void run(String errorData, Throwable exception) {
				
				Log.severe("++++getting sssfile " + fileURL
						+ " failed+++++");

				Log.severe("errorData= " + errorData);

				Log.severe("exception= " + exception.getLocalizedMessage());
				
				//LeftToLoad--;
				globalIndexsLeftToLoad--;
				IndexsToLoadList.remove(IndexURL);
				
				SuperSimpleSemantics.setCurrentLoadProcess("Filed to recieve file");
				//sub from loading widget
				SuperSimpleSemantics.stepLoadClockForward();
				
			}

		};
		
		boolean forcePost = false;

		if (fileURL.contains(":")){
			forcePost = true;
		}
		
		Log.info("loading new index");
		//using the above, try to get the text!
		SuperSimpleSemantics.fileManager.getText(fileURL,
				onResponse,
				onError,
				forcePost);

		
		/*
		
		RawQueryUtilities.GetTextFromRemoteURL(fileURL, new RequestCallback() {

				public void onError(Request request, Throwable exception) {
					Log.info("getting sssfile " + fileURL
							+ " failed");

					LeftToLoad--;
					globalLeftToLoad--;

					TrustedIndexList.loadingicon.setCurrentProcess("Filed to recieve file");
					//sub from loading widget
					TrustedIndexList.loadingicon.stepClockForward();

				}

				public void onResponseReceived(Request request,
						final Response response) {

					TrustedIndexList.loadingicon.setCurrentProcess("Recieved file");
					

					if (response.getStatusCode()==Response.SC_NOT_FOUND){
						
						Log.info("no index file found ");
						
						LeftToLoad--;
						globalLeftToLoad--;
						
						//sub from loading widget
						TrustedIndexList.loadingicon.stepClockForward();
						
						return;
					}
					
					Log.info("index file found " + response.getText());

					// process file
					
					//we have this deferred to give the GUI ( and thus loading icon) time
					// to update.
										
					Scheduler.get().scheduleDeferred(new ScheduledCommand() {    					
						@Override
						public void execute() {
							TrustedIndexList.loadingicon.setCurrentProcess("processing recieved index file");
							
								processIndexFile(response);
						}});
					
				}

				public void processIndexFile(Response response) {
					
					
					// the domain should be the default namespace for this set
					// of nodes					
					parseSSSIndexFile(response.getText(), IndexsDefaultNameSpace, postIndexProcessFunctions);
					
					
					
				}
			});

		*/
	}
protected void parseSSSIndexFile(String text, String ns, final Runnable postLoadCommands) {
	
		//ensure it always ends with a newline
		text=text+"\r";
		
		// split by lines:
		final String lines[] = text.split("\r?\n|\r");		
	
		Log.info("lines="+lines.length);

		//add to the loading widget
		SuperSimpleSemantics.addToTotalLoadUnits(lines.length);
		SuperSimpleSemantics.setCurrentLoadProcess("processing SSS Index");
		
		
		SuperSimpleSemantics.waitForRep.scheduleAfter(new MyRepeatingCommand() {			
			int i=0; //current line
			SSSTriplet lastLine=null;
			
			@Override
			public boolean execute() {

				//sub from loading
				SuperSimpleSemantics.stepLoadClockForward();
				
				//if at end we stop
				if (i >= lines.length){
					//run the post commands
					postLoadCommands.run();
					//exit
					return false;
				}
				
				//get current line
				String currentLine = lines[i].trim();

				Log.info("currentLine="+currentLine);
				Log.info("i="+i);
				
				//add one to line
				i++;
								
				
				// if empty continue
				if (currentLine.length()<3 || currentLine.startsWith("//") ) {					
					return true;
				}
				
				
				// if its a prefix
				if (currentLine.startsWith("@prefix")) {

					currentLine = currentLine.replaceAll("@prefix ", "");
					String[] linebits = currentLine.split("<");
					String prefix = linebits[0].trim();
					
					String uri = linebits[1].trim();
					//remove ending >
					int endsat  = uri.indexOf(">");
					uri=uri.substring(0, endsat);
					
					Log.info("loadstatements_loading prefixs from line p:::" + prefix
							+ " u:" + uri);
					RawQueryUtilities.addPrefix(uri, prefix);
					
					
				} else {

					// add the url to the index
					Log.info("adding line to index:" + currentLine);
					
						
						
						if (currentLine.endsWith(";")){
							//if it ends in ";" the next statements are part of the same set (that is, apply to the same url)
							//remove the ; first
							currentLine=currentLine.substring(0, currentLine.length()-1);
							
							
							lastLine =addStatementFromString(currentLine,lastLine);

					} else {
						//if line ends in "." or nothing, then its the end of a set of statements
						if (currentLine.endsWith(".")){
							currentLine=currentLine.substring(0, currentLine.length()-1);
							Log.info("cline:"+currentLine);
							
							
						}
						
						addStatementFromString(currentLine,lastLine);
						lastLine = null;
					}
				}
				
				//retrigger the loop with this
				return true;
			}
			
		});
		
		/*
		
		//set up a repeating command to process each line
		Scheduler.get().scheduleIncremental(new RepeatingCommand(){
			int i=0; //current line
			SSSTriplet lastLine=null;
			
			@Override
			public boolean execute() {

				//sub from loading
				GreenFruitEnginev3.stepLoadClockForward();
				
				//get current line
				String currentLine = lines[i].trim();
				//add one to line
				i++;
								
				//if at end we stop
				if (i >= lines.length){
					//run the post commands
					postLoadCommands.run();
					//exit
					return false;
				}
				// if empty continue
				if (currentLine.length()<3 || currentLine.startsWith("//") ) {					
					return true;
				}
				
				
				// if its a prefix
				if (currentLine.startsWith("@prefix")) {

					currentLine = currentLine.replaceAll("@prefix ", "");
					String[] linebits = currentLine.split("<");
					String prefix = linebits[0].trim();
					
					String uri = linebits[1].trim();
					//remove ending >
					int endsat  = uri.indexOf(">");
					uri=uri.substring(0, endsat);
					
					Log.info("loadstatements_loading prefixs from line p:::" + prefix
							+ " u:" + uri);
					RawQueryUtilities.addPrefix(uri, prefix);
					Log.info("i="+i);
					
				} else {

					// add the url to the index
					Log.info("adding line to index:" + currentLine);
					
						
						
						if (currentLine.endsWith(";")){
							//if it ends in ";" the next statements are part of the same set (that is, apply to the same url)
							//remove the ; first
							currentLine=currentLine.substring(0, currentLine.length()-1);
							
							
							lastLine =addStatementFromString(currentLine,lastLine);

					} else {
						//if line ends in "." or nothing, then its the end of a set of statements
						if (currentLine.endsWith(".")){
							currentLine=currentLine.substring(0, currentLine.length()-1);
							Log.info("cline:"+currentLine);
							
							
						}
						
						addStatementFromString(currentLine,lastLine);
						lastLine = null;
					}
				}
				
				//retrigger the loop with this
				return true;
			}
			
		});
		
		*/
		/*
		// loop over lines
		int i = 0;		
		while (i < lines.length) {
			
			

			//sub from loading
			TrustedIndexList.loadingicon.stepClockForward();

			String currentLine = lines[i].trim();

			i++;
			// if empty continue
			if (currentLine.length()<3 || currentLine.startsWith("//") ) {
				
				continue;
			}

			// if its a prefix
			if (currentLine.startsWith("@prefix")) {

				currentLine = currentLine.replaceAll("@prefix ", "");
				String[] linebits = currentLine.split("<");
				String prefix = linebits[0].trim();
				
				String uri = linebits[1].trim();
				//remove ending >
				int endsat  = uri.indexOf(">");
				uri=uri.substring(0, endsat);
				
				Log.info("loadstatements_loading prefixs from line p:::" + prefix
						+ " u:" + uri);
				RawQueryUtilities.addPrefix(uri, prefix);
				Log.info("i="+i);
				
			} else {

				// add the url to the index
				Log.info("adding line to index:" + currentLine);
				
					
					
					if (currentLine.endsWith(";")){
						//if it ends in ";" the next statements are part of the same set (that is, apply to the same url)
						//remove the ; first
						currentLine=currentLine.substring(0, currentLine.length()-1);
						
						
						lastLine =addStatementFromString(currentLine,lastLine);

				} else {
					//if line ends in "." or nothing, then its the end of a set of statements
					if (currentLine.endsWith(".")){
						currentLine=currentLine.substring(0, currentLine.length()-1);
						Log.info("cline:"+currentLine);
						
						
					}
					
					addStatementFromString(currentLine,lastLine);
					lastLine = null;
				}
			
			}

		}

		*/
		
	}
/*

	protected void parseSSSIndexFile_old(String text, String ns, Runnable postLoadCommands) {
		
		
		// split by lines:
		String lines[] = text.split("\r?\n|\r");
		SSSTriplet lastLine=null;
		

		//add to the loading widget
		SuperSimpleSemantics.addToTotalLoadUnits(lines.length);
		SuperSimpleSemantics.setCurrentLoadProcess("processing SSS Index");
		
		// loop over lines
		int i = 0;		
		while (i < lines.length) {
			
			

			//sub from loading
			SuperSimpleSemantics.stepLoadClockForward();

			String currentLine = lines[i].trim();

			i++;
			// if empty continue
			if (currentLine.length()<3 || currentLine.startsWith("//") ) {
				
				continue;
			}

			// if its a prefix
			if (currentLine.startsWith("@prefix")) {

				currentLine = currentLine.replaceAll("@prefix ", "");
				String[] linebits = currentLine.split("<");
				String prefix = linebits[0].trim();
				
				String uri = linebits[1].trim();
				//remove ending >
				int endsat  = uri.indexOf(">");
				uri=uri.substring(0, endsat);
				
				Log.info("loadstatements_loading prefixs from line p:::" + prefix
						+ " u:" + uri);
				RawQueryUtilities.addPrefix(uri, prefix);
				Log.info("i="+i);
				
			} else {

				// add the url to the index
				Log.info("adding line to index:" + currentLine);
				
					
					
					if (currentLine.endsWith(";")){
						//if it ends in ";" the next statements are part of the same set (that is, apply to the same url)
						//remove the ; first
						currentLine=currentLine.substring(0, currentLine.length()-1);
						
						
						lastLine =addStatementFromString(currentLine,lastLine);

				} else {
					//if line ends in "." or nothing, then its the end of a set of statements
					if (currentLine.endsWith(".")){
						currentLine=currentLine.substring(0, currentLine.length()-1);
						Log.info("cline:"+currentLine);
						
						
					}
					
					addStatementFromString(currentLine,lastLine);
					lastLine = null;
				}
			
			}

		}

		
		postLoadCommands.run();
	}

	
	**/
	
	public SSSTriplet addStatementFromString(String line, SSSTriplet lastLine) {
		
		
		
		Log.info("adding statement from line:" + line);

		String[] linebits = line.split(" ");

		// loop over adding them correctly (we dont assume single spaces
		// Separate the s p o)
		int i = 0;
		int fragment = 0;
		
		//Fragments are nodes, numbered in the order they appear in the line
		String fragment0=null; 
		String fragment1=null;
		String fragment2=null;
		String fragment3=null;
		String linebit="";
		boolean quoteOpen = false;
		while (i < linebits.length) {

			linebit = (linebit+" "+linebits[i]).trim();
			i++;
			
			//Log.info("linebit="+linebit);
			
			//ensure it has data
			if (linebit.isEmpty()) {
				continue;
			}
			//if it starts with a quote we loop till we find the end of the quote before assigning it to linebit
			
			
			if (linebit.startsWith("\"")&& !quoteOpen){		
				//Log.info("quoted detected ="+linebit);
				//ensure it doesn't also end the quote before flagging quote as open
				if (!linebit.substring(1).contains("\"")){
					Log.info("quoted detected still open="+linebit);
				quoteOpen = true;
				}
				
			} else if (linebit.substring(1).contains("\"")&& quoteOpen){
				Log.info("full quoted line ="+linebit);
				quoteOpen =false;
			}
			
			if (quoteOpen){				
				continue;
			}
			
			

			if (fragment == 0) {
				fragment0 = linebit.trim();
				// expand uri if known

			}
			if (fragment == 1) {
				fragment1 = linebit.trim();

			}
			if (fragment == 2) {
				fragment2 = linebit.trim();

				break;
			}
			linebit="";
			fragment++;

		}
		
		//by default the fragments are in this order
		String subject = fragment0;
		String predicate = fragment1;
		String value = fragment2;

		//if there was only two  fragments (and no previous statements) then its a subclass of something.
		//eg. isFlamable.txt Flammable 
		if ((lastLine==null)&&(fragment2==null)){
			Log.info("node is classOf"+fragment2);
			subject = fragment0;
			predicate=null;
			value = fragment1;
		}
		
		//if there was a previous line, we get subject from that and add the new pred/value as equilivents
		if (lastLine!=null){
			
			subject=lastLine.subject.PURI;
			predicate=fragment0;
			value=fragment1;
			
			
			Log.info("pred="+predicate);
			Log.info("value="+value);
			
			//deal with pred
			
			//if pred is quoted, its a label
			if (predicate.startsWith("\"")){
				lastLine.precident.addLabel(predicate);
				
			}

			//deal with value
			
			//if value is quoted, its a label
			if (value.startsWith("\"")){
				lastLine.value.addLabel(value);
				
			}
			return lastLine;
		} else {
		
			//strip quotes from node uris if present
			//predicate = RawQueryUtilities.stripQuotes(predicate);
			//value  = RawQueryUtilities.stripQuotes(value);
			//subject  = RawQueryUtilities.stripQuotes(subject);
			
			Log.info("pred="+predicate);
			Log.info("value="+value);
			Log.info("subject="+subject);
			
		// create nodes from data
			//added the extra Or here in case the pred is specified as a subclass
			SSSNode predicateNode;
			if (predicate!=null && !predicate.equalsIgnoreCase("rdfs:subClassOf")){
				//used to specify predicate on the label field separately.
				//predicateNode = SSSNode.createSSSNode(predicate.toLowerCase(), IndexsDefaultNameSpace);
				String newpredlabel = SSSNode.extractLabelFromURI(predicate);
				predicateNode = SSSNode.createSSSNode(newpredlabel, predicate.toLowerCase(), IndexsDefaultNameSpace);
			} else {
				//its a class of
				 predicateNode = SSSNode.SubClassOf;
				
			}
		
			//we specify the value on the label separately because we want it to maintain its case
			//if we use the default value but parse the uri as lowercase (which we want)
			//then the label would also be lower case
			String newvallabel = SSSNode.extractLabelFromURI(value);
			
		//SSSNode valueNode = SSSNode.createSSSNode(value.toLowerCase(), IndexsDefaultNameSpace);
			
			SSSNode valueNode = SSSNode.createSSSNode(newvallabel, value.toLowerCase(), IndexsDefaultNameSpace); 
		
		
		
//		SSSNode predicateNode = SSSNode.getNodeByUri(IndexsDefaultNameSpace
//				+ predicate);
//		if (predicateNode == null) {
//
//			predicateNode = new SSSNode(predicate, IndexsDefaultNameSpace
//					+ predicate.toLowerCase());
//
//		}
		// create nodes from data
//		SSSNode objectNode = SSSNode.getNodeByUri(IndexsDefaultNameSpace
//				+ object);
//		if (objectNode == null) {
//
//			objectNode = new SSSNode(object, IndexsDefaultNameSpace + object.toLowerCase());
//
//		}
		
		
		//detect if the subject is not a list, but a single node to create and add to a list
		/*
		if (!subject.contains(".")){
			
			Log.info("single node property specified within index");
			//create node
			SSSNode subjectNode = SSSNode.createSSSNode(subject, subject.toLowerCase(), IndexsDefaultNameSpace);
			subjectNode.add
			
			
		} else {	
		
		
		
		}*/
		
		addCommonPropertyListLocation(subject, predicateNode, valueNode);
		
		//Log.warning("*************************************************************creating:"+subject);
		//changed from directly making an SSSNode to prevent duplicates
		SSSNode subjectOrFile = SSSNode.createSSSNode(subject,IndexsDefaultNameSpace);
		
	//	 SSSTriplet newtriplet = new SSSTriplet(new SSSNode(subject,subject), predicateNode, valueNode);
		SSSTriplet newtriplet = new SSSTriplet(subjectOrFile, predicateNode, valueNode);
		 
		 return newtriplet;
		}
		
		
	}



	public void addCommonPropertyListLocation(String url, SSSNode pred,
			SSSNode value) {

		
		// made node from url  (note, we only add the IndexsDefaultDirectory if its not already a full, absolute path)
		SSSNode location;
		
		//if its a filename (which most things in the index should be)
		//we add the default directory to its PURI, unless its got a :
		//in which case we assume its got a prefix for a full url already
		
		//Filee name is determined by having a "." but not a "#"
		//This means fruit.txt will be detected as a file
		//but fruit.n3#apple will be detected as a url
		
		//^.*[/][^/]*\.[^/]*$					
		//boolean isFileName = ("/"+url).matches("^.*[/][^/]*\\.[^/]*$");
		
		boolean isFileName  = Utilitys.isFilename(url);
				
		//used to specify by url,url,Indexs...
		//now we just use url and create internally makes the label
		if ((url.contains(":")) || !isFileName){
		 location = SSSNode.createSSSNode(url,  IndexsDefaultNameSpace);
		} else {
			
			//if it is a file and it has no directory add the dafault one
			if (isFileName && (!url.contains("\\")||!url.contains("/")))
			{
				
				Log.info("adding IndexsDefaultDirectory name:"+IndexsDefaultDirectory);
				
				url=IndexsDefaultDirectory+url;
			}
			
			location = SSSNode.createSSSNode(url,  IndexsDefaultDirectory);
		 
		 
		}
		
		//SSSNode location = new SSSNode(url, IndexsDefaultDirectory+url); // we might want to assign a
													// standard "link class" as
													// a parent?(if one exists)
		
		


		Log.info("location ="+location.PURI);
		
		
		SSSTriplet newline = new SSSTriplet(location, pred, value);

		this.add(newline);

	}
	
	static public  ArrayList<String> getAllSSSFileURLsFor( SSSNode pred,SSSNode value){
		
		//results
		 ArrayList<String>  results = new  ArrayList<String>();
		
		//Iterate over all indexes and return combined results
		Iterator<SSSIndex> allKI = allKnowenIndexs.iterator();
		
		
		while (allKI.hasNext()) {
			SSSIndex si = (SSSIndex) allKI.next();
						
			
			
			results.addAll(si.getSSSFileURLsFor(pred, value));
			
		}
		
		return results;
	}

	public  ArrayList<String> getSSSFileURLsFor( SSSNode pred,SSSNode value){

		Log.info("getting urls for :"+pred.PURI+" "+value.PURI);
		
		
		 ArrayList<String> results = new  ArrayList<String>();
		
		Iterator<SSSTriplet> tit = this.iterator();
		
		while (tit.hasNext()) {
			
			SSSTriplet sssTriplet = (SSSTriplet) tit.next();

			Log.info("testing against:"+sssTriplet.subject.PURI);
			Log.info("testing against:"+sssTriplet.value.PURI);
			Log.info("testing against:"+sssTriplet.precident.PURI);
			sssTriplet.value.updateCachesOnNextUse();
			if ((sssTriplet.precident==pred)&&(sssTriplet.value.isOrHasParentClass(value.getPURI())))
			{
				
				Log.info("index of matching nodes:"+sssTriplet.subject.PURI);
				results.add(sssTriplet.subject.PURI);
			}
			
			
		}
		return results;
		
		
	}

	/** parse's all the known indexes.
	 * Note; They are only actually loaded if preloading is set. 
	 * as in a big database this would waste a lot of ram **/
	static public void parseAllKnownSSSFilesFromIndexs(){
		
		Iterator<SSSIndex> allKI = allKnowenIndexs.iterator();
		
		Log.warning("+++++++++++++++++parsing indexs "+allKnowenIndexs.size()+" in total");
	
		//add them all to the loading
		SSSNodesWithCommonProperty.addToLeftToLoadList(allKnowenIndexs);
		
		while (allKI.hasNext()) {
			
			SSSIndex si = (SSSIndex) allKI.next();			
			si.parseAllKnownSSSFilesFromIndex();			
						
		}
		
		
	}

	public static void setDebugPanel(GenericDebugDisplayer debugPanel){
		DebugPanel=debugPanel;
	}
	
	private static void info(String string) {
		
		//if theres a debug panel set, we log to it
		if (DebugPanel!=null){
			
			DebugPanel.log(string);
		}
		
		
	}
	
	private static void log(String string,String color) {
		
		//if theres a debug panel set, we log to it
		if (DebugPanel!=null){
			
			DebugPanel.log(string,color);
		}
		
		
	}
private static void error(String string) {
		
		//if theres a debug panel set, we log to it
		if (DebugPanel!=null){
			
			DebugPanel.error(string);
		}
		
		
	}
	/** loads all sssfiles  after index loading is done, this is only for testing/development
	 * as in a big database this would waste a lot of ram **/
	public void parseAllKnownSSSFilesFromIndex() {
		
		log("Parsing SSS files from this index:"+this.IndexsDefaultNameSpace,"Green");
		Log.info("parsing index: "+this.IndexsDefaultNameSpace);
		
		
		
	Iterator<SSSTriplet> tit = this.iterator();
	
	//add to loading widget
	SuperSimpleSemantics.addToTotalLoadUnits(this.size());
	
	//add to global loading 	
	SSSNodesWithCommonProperty.addToGlobalLeftToLoad(this.size());	
	
	//This is probably redundant, as if this parse was called as part of a "load all indexs" function
	//then that function would have added all the things to the loading list already.
	//
	SSSNodesWithCommonProperty.addToLeftToLoadList(this);
	
			
	Log.info("Loading index of size;"+this.size());
	
		while (tit.hasNext()) {
			
			final SSSTriplet sssTriplet = (SSSTriplet) tit.next();
									
			
			//SSSNodesWithCommonProperty newNodeSet = new SSSNodesWithCommonProperty(
		//			sssTriplet.precident,sssTriplet.value);
			

			info("Creating common property list for :"+sssTriplet.precident.PLabel+"."+sssTriplet.value.PLabel);
			info("Creating common property list for ("+sssTriplet.precident.PURI+"."+sssTriplet.value.PURI+")");
			
			Log.warning("Creating common property list for ("+sssTriplet.precident.PURI+"."+sssTriplet.value.PURI+")");
			
			SuperSimpleSemantics.waitFor.scheduleAfter(new Runnable() {
				
				@Override
				public void run() {
					
					SSSNodesWithCommonProperty newNodeSet = SSSNodesWithCommonProperty.createSSSNodesWithCommonProperty(sssTriplet.precident,sssTriplet.value);
										
					SuperSimpleSemantics.info("adding nodeset:"+sssTriplet.subject.PURI);
					
					//ensure its a file then load
					//This is determined by having one "." after the last  slash but not a "#"
					//This means fruit.txt will be detected as a file
					//but fruit.n3#apple will be detected as a url/uri
					
					//^.*[/][^/]*\.[^/]*$					
					//boolean isFileName = ("/"+sssTriplet.subject.PURI).matches("^.*[/][^/]*\\.[^/]*$");
					
					//if it has a "://" in it we ensure theres at least one other slash.
					//ie http://lostagain.nl is not a filename but http://lostagain.nl/test.txt is.
					String filenameTotest = sssTriplet.subject.PURI;
					
					//we now use filename detection in utilites rather then the regex that always went wrong
					boolean isFileName  = Utilitys.isFilename(filenameTotest);
					
					
					
					
					if (!isFileName){
						
						Log.info(sssTriplet.subject.PURI+"  is not filename");
						
					}
					
							//sssTriplet.subject.PURI.contains(".")
					if (isFileName && (!sssTriplet.subject.PURI.contains("#"))  )
					{
						Log.info("filename is"+sssTriplet.subject.PURI);
						
						//the URI should already be a full filename here
						//Default ns is added elsewhere on creation
						String fullfilename = sssTriplet.subject.PURI; //ensureFullFilename(sssTriplet.subject.PURI);
						
						Log.info("fullfilename is"+fullfilename);
						
						//its a file so we add its source
						newNodeSet.addFileSource(fullfilename,getIndexsDefaultNameSpace());
						
						//if preloading 
						if (SuperSimpleSemantics.preloadIndexs || 
							newNodeSet.getCommonPrec() == SSSNode.Equivlient ||
							( newNodeSet.getCommonPrec() == SSSNode.Label && SuperSimpleSemantics.autoloadLabels )							
							)
						{
							
							newNodeSet.loadSSSFile(fullfilename,getIndexsDefaultNameSpace());							
							Log.info("_globalLeftToLoad-:"+SSSIndex.getGlobalIndexsLeftToLoad());
							
						} else {
							//else we just add to lists and check if we are done
							SSSNodesWithCommonProperty.subtractFromGlobalLeftToLoad(1);
							SSSNodesWithCommonProperty.removeFromLeftToLoadList(sssTriplet);
							
						//	Log.warning("temp1");
							newNodeSet.checkLeftToLoad(sssTriplet.subject.PLabel);
							SSSNodesWithCommonProperty.checkIfFinnishedGlobal();
							
						
							newNodeSet.isLoaded = false;
							Log.info("globalLeftToLoad~:"+globalIndexsLeftToLoad+" newNodeSet.isLoaded="+newNodeSet.isLoaded );
						}
						
						
					}  else 
					{
						//if its not a file we just add it directly
						Log.info("adding node to set :"+sssTriplet.subject);
						
						newNodeSet.add(sssTriplet.subject);
						SSSNodesWithCommonProperty.subtractFromGlobalLeftToLoad(1);

						SSSNodesWithCommonProperty.removeFromLeftToLoadList(sssTriplet);
						Log.info("globalLeftToLoad :"+globalIndexsLeftToLoad);
						newNodeSet.checkLeftToLoad(sssTriplet.subject.PLabel);

						Log.warning("temp2");
						SSSNodesWithCommonProperty.checkIfFinnishedGlobal();
						
					}
					
					SuperSimpleSemantics.stepLoadClockForward();
					
				}
				
			});
			
			
		
			
			
		}
		

	}
	
	/** ensures a full, absolute path is specified
	 * if not, it adds the indexs location as the assumed directory **/
	protected String ensureFullFilename(String pURI) {
		
		if (pURI.contains(":")){
			
			return pURI;
			
		} else {			
			
			Log.info("adding default directory "+getIndexsDefaultDirectory());
			
			pURI = getIndexsDefaultDirectory()+pURI;
		}
		 
		return pURI;
	}

	/** Loads the indexes (*.ntlist) at the specified locations<br>
	 * <br>
	 * How location strings are interpreted is determined by the supplied FileManager.<br>
	<br>
	 * (ie, if its local or a url)**/
	public static void loadIndexsAt(ArrayList<String> trustedIndexs) {
			
		ArrayList<String> absolute_trusted_index_urls = new ArrayList<String>();
		
		//convert all to absolute if needed
		for (final String requestedurl : trustedIndexs) {
			
			//ensure its a full domain or file location
			//this is to make sure when nodes dont specify a uri
			//what they are assumed to be isnt amibiogus 
			String siteurl = SuperSimpleSemantics.fileManager.getAbsolutePath(requestedurl);
			
			absolute_trusted_index_urls.add(siteurl);
		}
		
		
		IndexsToLoadList.addAll(absolute_trusted_index_urls); //remove as temp
			
		//SSSIndex.addToIndexsLeftToLoad(trustedIndexs.size());
		
		//load specified ones
		for (final String abs_trusted_url : absolute_trusted_index_urls) {
			
			Log.info("__________________________________loading index at:"+abs_trusted_url);

		
			
	
			SuperSimpleSemantics.setCurrentLoadProcess("Loading trusted indexs "+abs_trusted_url);
			
			SuperSimpleSemantics.waitFor.scheduleAfter(new Runnable() {				
				@Override
				public void run() {
					
				//	final SSSIndex testindex = new SSSIndex(siteurl);	
					
					createNewIndex(abs_trusted_url);
					
				}
			});
			
		
			
		}
		
		
		
	}

	/** Loads a single index. the string supplied should be a *.ntlist file
	 *  (currently untested, if in dought use the loadIndexs function and just give it one index)
	 * */
	public static void loadIndexAt(String trustedIndex) {
		
		//ensure its a full domain or file location
		//this is to make sure when nodes dont specify a uri
		//what they are assumed to be isnt amibiogus 
		final String siteurl = SuperSimpleSemantics.fileManager.getAbsolutePath(trustedIndex);			

		IndexsToLoadList.add(siteurl);// removed temp
		
		

		Log.info("__________________________________loading index at:"+siteurl);

		SuperSimpleSemantics.setCurrentLoadProcess("Loading trusted indexs "+siteurl);
		
		SuperSimpleSemantics.waitFor.scheduleAfter(new Runnable() {				
			@Override
			public void run() {
				
				
				createNewIndex(siteurl);
				
			}
		});
		
		
	}
	
	
	public static void clearAllIndexs() {
	
		allKnowenIndexs.clear();
		
	}

	public static void clearLoadingList() {
		IndexsToLoadList.clear();
		globalIndexsLeftToLoad=0;
	}

	
	/**
	 * returns the index specified by the supplied url
	 * 
	 * @param indexsAbsoluteUrl
	 * @return
	 */
	static public SSSIndex getIndex(String indexsAbsoluteUrl){
		
		//ensure full path was supplied
		indexsAbsoluteUrl = SuperSimpleSemantics.fileManager.getAbsolutePath(indexsAbsoluteUrl);

		//Log.info("testing index:"+indexsAbsoluteUrl);
		
		//look for and return requested index
		for (SSSIndex index : allKnowenIndexs) {

			//Log.info("known index:"+index.IndexURL);
			if (index.IndexURL.equals(indexsAbsoluteUrl)){
				return index;
			}
			
		}
		
		
		
		return null;
		
	}
	
	public static void replaceNodesInIndexs(SSSNode thisNode,
			SSSNode withThisNode) {
		
		//loop over each index
		for (SSSIndex indexToChangeNodesIn : allKnowenIndexs) {
			
			indexToChangeNodesIn.replaceNodes(thisNode,
					withThisNode);
			
		}
		
	}

	
	private void replaceNodes(SSSNode oldnode, SSSNode withThisNode) {
		
		//loop over each triplet, and replace if anything matchs
		//loop over checking
		for (SSSTriplet index_line_to_check : this) {
			
			//we only change or combine the set if it used the oldnode
			boolean changed = false;
			
			//Log.warning("checking set:"+set_to_check.getCommonPrec()+"="+set_to_check.getCommonValue());
			
			//if the old node is used as the predicate or value use the replacement instead
			if (index_line_to_check.subject==oldnode){
				index_line_to_check.subject=withThisNode;

				changed = true;
			}
			if (index_line_to_check.precident==oldnode){
				index_line_to_check.precident=withThisNode;

				changed = true;
			}

			if (index_line_to_check.value==oldnode){
				
				index_line_to_check.value=withThisNode;

				changed = true;
			}

			//we only change or combine the set if it used the oldnode
			if (changed){

				Log.warning("node changed in index" );
				


			}

		}
		
	}

	



	/** normally SSSFiles are finalised automatically when they are parsed.
	 * However, if we arnt preloaded, we cant parse yet, so we finallise here.
	 * Eventually this will allow dynamic loading.
	 * Finalising essentially means adding to the internal triplet lists and saying they are ready for querys. 
	public static void FinaliseAllCommonPropertySets() {
		
		
		Iterator<SSSIndex> allKI = allKnowenIndexs.iterator();
			
		Log.info("finalise all indexs "+allKnowenIndexs.size()+" in total");
		
			
			while (allKI.hasNext()) {
				
				SSSIndex si = (SSSIndex) allKI.next();
				
				
				si.checkLeftToLoad("FileNameNotSupportedYet");
			
							
			}
			SSSNodesWithCommonProperty.checkIfFinnishedGlobal();
		
	}**/

}
